import zmq
import sys
import threading
import time
from random import randint, random


def tprint(msg):
    sys.stdout.write(msg + '\n')
    sys.stdout.flush()


class ClientTask(threading.Thread):
    def __init__(self, id):
        self.id = id
        threading.Thread.__init__(self)

    def run(self):
        context = zmq.Context()
        socket = context.socket(zmq.DEALER)
        identity = 'client%d' % self.id
        socket.identity = identity.encode("utf-8")
        socket.connect("tcp://localhost:5570")
        print('Client %s started' % identity)
        poll = zmq.Poller()
        poll.register(socket, zmq.POLLIN)
        reqs = 0
        while True:
            reqs = reqs + 1
            print('Client %s Req #%d sent...' % (identity, reqs))
            socket.send_string('Client %s request #%d' % (identity, reqs))
            for i in range(5):
                sockets = dict(poll.poll(1000))
                if socket in sockets:
                    msg = socket.recv()
                    tprint('Client %s received: %s' % (identity, msg))
        socket.close()
        context.destroy()


class ServerTask(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        context = zmq.Context()
        frontend = context.socket(zmq.ROUTER)
        frontend.bind("tcp://*:5570")

        backend = context.socket(zmq.DEALER)
        backend.bind("inproc://backend")

        workers = []
        for i in range(5):
            worker = ServerWorker(context, i)
            worker.start()
            workers.append(worker)

        zmq.proxy(frontend, backend)

        frontend.close()
        backend.close()
        context.destroy()


class ServerWorker(threading.Thread):
    def __init__(self, context, id):
        threading.Thread.__init__(self)
        self.context = context
        self.id = id

    def run(self):
        worker = self.context.socket(zmq.DEALER)
        worker.connect("inproc://backend")
        tprint("Worker worker-{} started".format(self.id))
        while True:
            ident, msg = worker.recv_multipart()
            tprint("Worker worker%d received %s from %s" % (self.id, msg, ident))
            replies = randint(0, 4)
            tprint("Worker worker%d replies %d" % (self.id, replies))
            for i in range(replies):
                time.sleep(1. / (randint(1, 10)))
                print('Worker worker%d Rep %s send %s' % (self.id, ident, msg))
                worker.send_multipart([ident, msg])
        worker.close()


def main():
    server = ServerTask()
    server.start()

    for i in range(3):
        client = ClientTask(i)
        client.start()

    server.join()


if __name__ == "__main__":
    main()
